/*
 * Copyright 2009 Original Author(s)
 * 
 * This file is part of Kommando
 * 
 * Kommando is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Kommando is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Kommando.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kommando.ui;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.ImageIcon;

import org.kommando.core.Displayable;

/**
 * @author Peter De Bruycker
 */
public class IconHelper {

    public static Icon getIcon(Displayable displayable, Dimension size) {
        Icon source = displayable.getIcon();

        if (source == null) {
            return new EmptyIcon(size);
        } else {
            if (size.width == source.getIconWidth() && size.height == source.getIconHeight()) {
                // perfect match
                return source;
            } else if (size.width < source.getIconWidth() || size.height < source.getIconHeight()) {
                // icon is too big, scale it down
                BufferedImage image = new BufferedImage(source.getIconWidth(), source.getIconHeight(),
                        BufferedImage.TYPE_INT_ARGB);
                Graphics2D graphics = (Graphics2D) image.getGraphics();
                source.paintIcon(null, graphics, 0, 0);
                graphics.dispose();

                // TODO keep proportional size
                BufferedImage scaledInstance = getScaledInstance(image, size.width, size.height,
                        RenderingHints.VALUE_INTERPOLATION_BICUBIC, true);

                return new ImageIcon(scaledInstance);
            } else {
                // icon is too small, pad it to the correct size
                return new PaddingIcon(source, size);
            }
        }
    }

    /**
     * Convenience method that returns a scaled instance of the provided {@code BufferedImage}.
     * 
     * @param img
     *            the original image to be scaled
     * @param targetWidth
     *            the desired width of the scaled instance, in pixels
     * @param targetHeight
     *            the desired height of the scaled instance, in pixels
     * @param hint
     *            one of the rendering hints that corresponds to {@code RenderingHints.KEY_INTERPOLATION} (e.g. {@code
     *            RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR}, {@code
     *            RenderingHints.VALUE_INTERPOLATION_BILINEAR}, {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
     * @param higherQuality
     *            if true, this method will use a multi-step scaling technique that provides higher quality than the
     *            usual one-step technique (only useful in downscaling cases, where {@code targetWidth} or {@code
     *            targetHeight} is smaller than the original dimensions, and generally only when the {@code BILINEAR}
     *            hint is specified)
     * @return a scaled version of the original {@code BufferedImage}
     */
    private static BufferedImage getScaledInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint,
            boolean higherQuality) {
        int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB
                : BufferedImage.TYPE_INT_ARGB;
        BufferedImage ret = (BufferedImage) img;
        int w, h;
        if (higherQuality) {
            // Use multi-step technique: start with original size, then
            // scale down in multiple passes with drawImage()
            // until the target size is reached
            w = img.getWidth();
            h = img.getHeight();
        } else {
            // Use one-step technique: scale directly from original
            // size to target size with a single drawImage() call
            w = targetWidth;
            h = targetHeight;
        }

        do {
            if (higherQuality && w > targetWidth) {
                w /= 2;
            }
            if (w < targetWidth) {
                w = targetWidth;
            }

            if (higherQuality && h > targetHeight) {
                h /= 2;
            }
            if (h < targetHeight) {
                h = targetHeight;
            }

            BufferedImage tmp = new BufferedImage(w, h, type);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(ret, 0, 0, w, h, null);
            g2.dispose();

            ret = tmp;
        } while (w != targetWidth || h != targetHeight);

        return ret;
    }

}
